イベントフローの確認
- 2010 年 1月 15 日
普段気にすることはありませんが、addEventListenerのuseCaptureについて。
AS3ではイベントが3段階で処理されている。原則的にイベントはステージから、イベントが発生したオブジェクトに向かい、オブジェクトに辿り着いたら今度はステージに戻ってくる。
入れ子になった表示オブジェクトは、この経路にしたがってイベントをリレーしているわけだ。
普段意識せず、useCapture = false のままに設定しているなら、イベントが発生したオブジェクトの段階か、ステージに戻ってくる段階の通知をキャッチしている。
このことを示した図がリファレンスに書かれている。
イベントフロー
この図はとてもよく説明されているのだけど、実際に動いているもので確認したくて試しました。
Eventオブジェクトには、イベントのリレーを止める、stopPropagationとstopImmediatePropagationってメソッドもあるので、この二つの挙動についても試している。
イベントフローを意識することがあるのは、入れ子になった表示オブジェクトの場合が想定されるので、大きい方から順番にA、B、Cと円を定義して、入れ子にする。
var A:Sprite = new Sprite(); A.name = "A"; var B:Sprite = new Sprite(); B.name = "B"; var C:Sprite = new Sprite(); C.name = "C"; A.graphics.lineStyle(5, 0x000000 ); A.graphics.beginFill( 0x000000, 0 ); A.graphics.drawCircle( 0,0, 100 ); B.graphics.lineStyle(2.5, 0x404040 ); B.graphics.beginFill( 0x404040, 0 ); B.graphics.drawCircle( 0,0, 75 ); C.graphics.lineStyle(1, 0x808080 ); C.graphics.beginFill( 0x808080, 0 ); C.graphics.drawCircle( 0,0, 50 ); //BはAの子、CはBの子 A.addChild( B ); B.addChild( C ); addChild( A ); |
全ての円にキャプチャ段階とターゲット・バブリング段階それぞれにリスナーを定義します。
stopPropagation、stopImmediatePropagationを試すべく、優先順位をつけてリスナーを二つ登録しました。
優先順位の高い方のリスナーは、ラジオボタンに応じてstopPropagation、stopImmediatePropagationが実行されるようにします。
リスナーはテキストエリアに処理された内容を表示します。
//優先度の高いリスナー A.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing1, true ); B.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing1, true ); C.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing1, true ); A.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget1, false ); B.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget1, false ); C.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget1, false ); //優先度の低いリスナー A.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing2, true, -1 ); B.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing2, true, -1 ); C.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing2, true, -1 ); A.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget2, false, -1 ); B.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget2, false, -1 ); C.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget2, false, -1 ); private function _onMouseDownCaptureing1(e:MouseEvent):void{ if( group1.selectedData == 1 ) e.stopPropagation(); else if( group1.selectedData == 2 ) e.stopImmediatePropagation(); var str:String = "#1 " + e.currentTarget["name"] + "がキャプチャ段階のリスナーで、" + e.target["name"] + "をターゲットに" + e.type + "イベントを" + _returnPhase(e) + "で処理。"; _field.appendText( str + "\n" ); } private function _onMouseDownTarget1(e:MouseEvent):void{ if( group2.selectedData == 1 ) e.stopPropagation(); else if( group2.selectedData == 2 ) e.stopImmediatePropagation(); var str:String = "#1 " + e.currentTarget["name"] + "がターゲット・バブリング段階のリスナーで、" + e.target["name"] + "をターゲットに" + e.type + "イベントを" + _returnPhase(e) + "で処理。"; _field.appendText( str + "\n" ); } private function _onMouseDownCaptureing2(e:MouseEvent):void{ var str:String = "#2 " + e.currentTarget["name"] + "がキャプチャ段階のリスナーで、" + e.target["name"] + "をターゲットに" + e.type + "イベントを" + _returnPhase(e) + "で処理。"; _field.appendText( str + "\n" ); } private function _onMouseDownTarget2(e:MouseEvent):void{ var str:String = "#2 " + e.currentTarget["name"] + "がターゲット・バブリング段階のリスナーで、" + e.target["name"] + "をターゲットに" + e.type + "イベントを" + _returnPhase(e) + "で処理。"; _field.appendText( str + "\n" ); } private function _returnPhase(e:MouseEvent):String{ return ( EventPhase.AT_TARGET == e.eventPhase ) ? "ターゲット段階" : ( EventPhase.BUBBLING_PHASE == e.eventPhase ) ? "バブリング段階" : "キャプチャ段階"; } |
こちらで試せます。場合によっては意図に反して2回処理されていたみたいな間違いはありそうだね。
こんなものを意識して何か作ることが無いことを祈るばかり。